<template>
  <div class="user-house wh-full">
    <div
      ref="userHousePanel"
      class="house-image"
      :style="`background-image: url(${userHouse?.image}) `"
    >
      <vue-drag-resize-rotate
        v-for="(device, index) in userDeviceList"
        :id="device.id"
        :key="device.id"
        :w="deviceSize"
        :h="deviceSize"
        :resizable="false"
        :parent="true"
        :draggable="draggable"
        :x="getDevicePos(device).x"
        :y="getDevicePos(device).y"
        @dragstop="(x: number, y: number) => onDragStop(index, x, y)"
      >
        <el-tooltip effect="dark" :content="device.device_name">
          <div
            class="my-device cursor-pointer flex-center"
            :class="{
              waring: isWaring(device),
              online: device.online_status === 1,
              offline: device.online_status === 0,
            }"
            @dblclick="onDeviceClick(device)"
          >
            <img :src="device.device_image" class="icon" />
          </div>
        </el-tooltip>
      </vue-drag-resize-rotate>
    </div>

    <!-- 设备基本信息弹窗 -->
    <el-dialog v-model="deviceInfoDialogVisible" append-to-body destroy-on-close width="35%">
      <UserDeviceCardInfo :sn="deviceInfoDialogSn" />
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import "@gausszhou/vue3-drag-resize-rotate/lib/bundle.esm.css";
import DeviceAPI from "@/api/device";
import HouseAPI from "@/api/house";
import UserAPI from "@/api/user";
import { UserDeviceModel } from "@/model/user-device-model";
import { HouseModel } from "@/model/house-model";
import VueDragResizeRotate from "@gausszhou/vue3-drag-resize-rotate";
import MqttClient from "@/utils/MqttClient";
import { isJsonString } from "@/utils/StringUtils";
import UserDeviceCardInfo from "../UserDeviceCardInfo/UserDeviceCardInfo.vue";
import { throttle } from "@/utils";

defineOptions({
  name: "UserHouse",
  components: {
    VueDragResizeRotate,
  },
});

const mqttTopic = "/adminWaring";
let mqttClient: MqttClient | undefined;
const deviceInfoDialogVisible = ref(false);
const deviceInfoDialogSn = ref("");

const userDeviceList = ref<UserDeviceModel[]>([]);
const userDeviceUpdateList: Record<string, { map_x: number; map_y: number }> = {};
const userHouse = ref<HouseModel>();

/**
 * @description 组件属性
 */
const props = defineProps({
  userId: { type: [Number, String], default: 0 },
  draggable: { type: Boolean, default: true },
});

const userHousePanel = ref<HTMLElement | null>(null);
// 面板尺寸
const panelSize = ref({ width: 0, height: 0 });
// 设备大小 根据面板尺寸计算
const deviceSize = computed(() => panelSize.value.width * 0.1);

/**
 * 根据设备的相对位置计算设备在面板上的实际位置
 *
 * @param {any} device - 设备对象，包含设备的相对位置信息（x和y坐标）
 * @returns {object} - 返回设备在面板上的实际位置对象，包含x和y坐标
 */
function getDevicePos(device: UserDeviceModel) {
  const deviceSize = {
    x: panelSize.value.width * (device.map_x || 0),
    y: panelSize.value.height * (device.map_y || 0),
  };

  return deviceSize;
}

/**
 * 获取面板的矩形信息
 * 此函数用于获取用户房屋面板的宽度和高度信息如果用户房屋面板不存在，则返回宽度和高度均为0的对象
 * 这对于在渲染前或面板未正确初始化时防止错误很有用
 *
 * @returns {Object} 面板的矩形信息，包含宽度和高度属性如果面板不存在，宽度和高度均为0
 */
function getPanelRect() {
  if (!userHousePanel.value) return { width: 0, height: 0 };
  const rect = userHousePanel.value.getBoundingClientRect();
  return {
    width: rect.width || 0,
    height: rect.width || 0,
  };
}

/**
 * @description 更新面板尺寸
 * @returns void
 */
function updatePanelSize() {
  panelSize.value = getPanelRect();
}

/**
 * 当拖拽停止时触发的函数
 *
 * @param index 拖拽元素的索引
 * @param x 拖拽结束时的水平位置
 * @param y 拖拽结束时的垂直位置
 *
 */
function onDragStop(index: number, x: number, y: number) {
  const device: UserDeviceModel = userDeviceList.value[index];
  const xRate = x / panelSize.value.width;
  const yRate = y / panelSize.value.height;

  // 使用设备的 ID 或其他唯一标识符作为键
  const sn = (device.device_sn || "").toString();
  userDeviceUpdateList[sn] = {
    map_x: xRate,
    map_y: yRate,
  };
}

/**
 * @description 获取用户户型数据,获取用户设备列表
 */
async function fetchData() {
  const user = await UserAPI.detail(props.userId);
  userHouse.value = user.user_apartment_renderings;
  // 过滤掉in_house !== 1的设备
  userDeviceList.value = user.user_device?.filter((device: any) => device.in_house === 1) || [];
}

/**
 * @description 刷新设备
 */
async function refreshDevices() {
  const user = await UserAPI.detail(props.userId);
  userDeviceList.value = user.user_device || [];
}

/**
 * @description 设备点击事件
 */
function onDeviceClick(device: UserDeviceModel) {
  deviceInfoDialogSn.value = device.device_sn || "";
  deviceInfoDialogVisible.value = true;
}

onMounted(() => {
  fetchData();

  initMqtt();

  // 监听窗口大小变化 重新计算面板尺寸
  window.addEventListener("resize", throttle(updatePanelSize, 500));
});

onUnmounted(() => {
  // 移除理监听器
  window.removeEventListener("resize", throttle(updatePanelSize, 500));

  if (mqttClient) {
    mqttClient.disconnect();
    mqttClient = undefined;
  }
});

// 监听 userHousePanel 何时有值
watch(
  userHousePanel,
  (newValue) => {
    if (newValue) {
      updatePanelSize();
    }
  },
  { immediate: true }
);

// 定义一个防抖函数，用于处理 MQTT 消息，并在 1 秒内只处理一次消息
// const debouncedMqttMessage = debounce(onMqttMessage.bind(this), 1000);

/**
 * @description 初始化 MQTT 连接
 */
async function initMqtt() {
  if (mqttClient) return;
  mqttClient = new MqttClient();

  try {
    await mqttClient.connect();
    await mqttClient.subscribe(mqttTopic);
    mqttClient.onMessage(onMqttMessage);
  } catch (error) {
    console.error("Error occurred:", error);
  }
}

/**
 * 处理接收到的MQTT消息
 *
 * @param {string} topic - 接收到的MQTT消息的主题
 * @param {Buffer} message - 接收到的MQTT消息内容，格式为Buffer
 */
function onMqttMessage(topic: string, message: Buffer) {
  console.log("Received MQTT message:", topic, message.toString());
  if (topic !== mqttTopic) return;
  if (!isJsonString(message.toString())) return;
  const messageData = JSON.parse(message.toString());
  const { name, content, color, delay } = messageData;
  const timer = setTimeout(() => {
    ElNotification({
      title: name,
      message: content,
      type: color,
    });
    updateDeviceState(messageData);
    clearTimeout(timer);
  }, delay);
}

/**
 * @description 更新设备状态
 * @param device
 * @returns
 */
function updateDeviceState(messageData: any) {
  const { sn, color } = messageData;
  const deviceIndex = userDeviceList.value.findIndex((d) => d.device_sn === sn);
  const device = userDeviceList.value[deviceIndex];
  if (!isJsonString(device.ext_data || "")) {
    device.ext_data = JSON.stringify({});
  }
  const extData = JSON.parse(device.ext_data || "");
  if (["error", "danger", "warning"].includes(color)) {
    extData.waring = 1;
  } else {
    extData.waring = 0;
  }
  device.ext_data = JSON.stringify(extData);
  userDeviceList.value.splice(deviceIndex, 1, device);
}

/**
 * @description 设置户型数据
 * @param {HouseModel} value
 */
function setUserHouse(value: HouseModel) {
  userHouse.value = value;
}

/**
 * @description 判断设备是否警告
 */
function isWaring(device: UserDeviceModel): Boolean {
  if (!isJsonString(device.ext_data || "")) {
    return false;
  }
  const extData = JSON.parse(device.ext_data || "");
  return extData.waring === 1;
}

/**
 * @description 保存户型数据 ， 保存设备位置
 */
async function saveUserHouse() {
  if (!userHouse.value) return;
  await Promise.all([
    HouseAPI.setUserHouse(props.userId ?? 0, userHouse.value.id ?? 0),
    DeviceAPI.updateDevicesPos(userDeviceUpdateList),
  ]);
  ElMessage.success("保存成功");
}

// 导出组件实例类型
export interface UserHouseInstance {
  updatePanelSize: () => void;
  setUserHouse: (value: HouseModel) => void;
  saveUserHouse: () => Promise<void>;
  refreshDevices: () => void;
}

defineExpose({
  updatePanelSize,
  setUserHouse,
  saveUserHouse,
  refreshDevices,
});
</script>

<style lang="scss" scoped>
.user-house {
  .house-image {
    position: relative;
    aspect-ratio: 1;
    width: 100%;
    background-size: 100% 100%;
    background-color: #fdf5e6;
    border: 1px solid #ffdab9;
  }
  .my-device {
    width: 100%;
    aspect-ratio: 1;
    border-radius: 50%;
    .icon {
      aspect-ratio: 1;
      width: 65%;
    }
  }

  .online {
    background-color: rgba($color: #159a0e, $alpha: 0.7); // 开灯效果
  }
  .offline {
    background-color: rgba($color: #000000, $alpha: 0.4); // 关灯效果
  }

  // 代表报警状态 需要红色背景闪烁效果
  .waring {
    animation: blink 1s linear infinite;
    @keyframes blink {
      0% {
        background-color: rgba($color: #ff0000, $alpha: 0.2);
      }

      50% {
        background-color: rgba($color: #ff0000, $alpha: 0.5);
      }

      100% {
        background-color: rgba($color: #ff0000, $alpha: 0.8);
      }
    }
  }
}
</style>
